﻿using System;
using System.Collections.Generic;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
using BlueDot.Data.Core;
using BlueDot.Data.Core.Sql;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServerCe;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Syncronization.Common;
using Syncronization.REST;
using Syncronization.Service.UnitTests.Model;
using Syncronization.Strategy;
using SyncTable=Syncronization.Service.UnitTests.Model.SyncTable;
using BlueDot.Data.Core.EntityModel;

namespace Syncronization.Service.UnitTests
{
    /// <summary>
    /// Summary description for RestSyncServiceFixture
    /// </summary>
    [TestClass]
    public class RestSyncServiceFixture
    {
        #region Tests

        [TestMethod]
        public void CanCompleteInitialSync()
        {
            var host = new WebServiceHost(typeof (RestSyncService), new Uri("http://localhost:7356"));
            if (host.State != CommunicationState.Opened)
                host.Open();

            SyncStatistics stats = null;
            try
            {
                var uri = new Uri("http://localhost:7356/GetChanges");

                File.Delete("GetChanges.sdf");

                DataStoreSchemaBuilder.AddCustomStrategies += (object sender, DataStoreShemaEventArgs args) =>  {
                                                                      var sqlProvider =  (IDataStoreSchemaProvider) sender;
                                                                      sqlProvider.Strategies.Add(new SyncServiceStrategy {SyncBaseType = typeof (SyncTable)});
                                                                  };

                ProviderFactory.DefaultProvider = new SqlClientProvider {
                                                          ConnectionString = "Data Source=.;Initial Catalog=RestSyncServiceFixture;Integrated Security=True;",
                                                          MasterDataBaseConnectionString = "Data Source=.;Initial Catalog=master;Integrated Security=True;"
                                                      };

                DataStoreSchemaBuilder.CreateDataStore(new List<Type> {typeof (User), typeof (User_Tombstone)},true);

                new User
                    {
                        UserName = "firstUser",  Password = "password",  FirstName = "first",  LastName = "User",
                        CreationDate = DateTime.Now,  ModifiedDate = DateTime.Now
                    }.Save();
                new User
                    {
                        UserName = "secondUser",  Password = "password",  FirstName = "second", LastName = "User",
                        CreationDate = DateTime.Now, ModifiedDate = DateTime.Now
                    }.Save();

                var syncProvider = new RestSyncProvider { GetChangesWebMethod = "GetChanges", BaseURI = new Uri("http://localhost:7356/") };
                var clientProvider = new SqlCeClientSyncProvider("Data Source = GetChanges.sdf", true);

                var agent = new mfLYSyncAgent(syncProvider, clientProvider)
                { 
                    TombstoneBaseType = typeof (Tombstone)
                };

                agent.Synchronize();

                agent.AddSchema(EntityMap.SchemaBuilders, SyncDirection.Bidirectional,
                                TableCreationOption.UploadExistingOrCreateNewTable, "Group1");

                stats = agent.Synchronize();
            }
            finally
            {
                host.Close();
            }

            Assert.IsNotNull(stats);
            Assert.AreEqual(2, stats.DownloadChangesApplied);

            ProviderFactory.DefaultProvider = new SqlCeProvider {ConnectionString = "Data Source = GetChanges.sdf"};
            var users = (List<User>) DataPersistence.Select<User>();

            Assert.AreEqual(2, users.Count);
            Assert.AreEqual("firstUser", users[0].UserName);
            Assert.AreEqual("secondUser", users[1].UserName);
        }

        [TestMethod]
        public void CanSendNewRecordsToServer()
        {
            var host = new WebServiceHost(typeof(RestSyncService), new Uri("http://localhost:7356"));
            if (host.State != CommunicationState.Opened)
                host.Open();

            SyncStatistics stats = null;
            try
            {
                var uri = new Uri("http://localhost:7356/GetChanges");

                File.Delete("GetChanges.sdf");

                var syncProvider = new RestSyncProvider { ApplyChangesWebMethod="ApplyChanges", GetChangesWebMethod = "GetChanges", BaseURI = new Uri("http://localhost:7356/") };

                var clientProvider = new SqlCeClientSyncProvider("Data Source = GetChanges.sdf", true);

                DataStoreSchemaBuilder.AddCustomStrategies += (object sender, DataStoreShemaEventArgs args) =>
                {
                    var sqlProvider =
                        (IDataStoreSchemaProvider)sender;

                    sqlProvider.Strategies.Add(
                        new SyncServiceStrategy { SyncBaseType = typeof(SyncTable) });
                };

                ProviderFactory.DefaultProvider = new SqlClientProvider
                {
                    ConnectionString =
                        "Data Source=.;Initial Catalog=RestSyncServiceFixture;Integrated Security=True;",
                    MasterDataBaseConnectionString =
                        "Data Source=.;Initial Catalog=master;Integrated Security=True;"
                };
                DataStoreSchemaBuilder.CreateDataStore(new List<Type> { typeof(User), typeof(User_Tombstone) },
                                                       true);

                new User
                {
                    UserName = "firstUser",
                    Password = "password",
                    FirstName = "first",
                    LastName = "User",
                    CreationDate = DateTime.Now,
                    ModifiedDate = DateTime.Now
                }.Save();
                new User
                {
                    UserName = "secondUser",
                    Password = "password",
                    FirstName = "second",
                    LastName = "User",
                    CreationDate = DateTime.Now,
                    ModifiedDate = DateTime.Now
                }.Save();

                var agent = new mfLYSyncAgent(syncProvider, clientProvider);
                agent.TombstoneBaseType = typeof(Tombstone);

                //you can set the SyncDirection to DownloadOnly and it does not make the CE table
                //read only.  Very COOL.  That way you can set the table direction based on 
                //role permissions.  If user logs in as admin then set it as bidirectional otherwise
                //changes won't be synced.

                //add schema builders
                agent.AddSchema(EntityMap.SchemaBuilders, SyncDirection.Bidirectional,
                                TableCreationOption.UploadExistingOrCreateNewTable, "Group1");

                stats = agent.Synchronize();

                var provider = new SqlCeProvider { ConnectionString = "Data Source = GetChanges.sdf" };
                new User
                {
                    UserName = "thirdUser",
                    Password = "password",
                    FirstName = "third",
                    LastName = "User",
                    CreationDate = DateTime.Now,
                    ModifiedDate = DateTime.Now
                }.Save(provider);

                stats = agent.Synchronize();

            }
            finally
            {
                host.Close();
            }

            var users = (List<User>)DataPersistence.Select<User>();

            Assert.AreEqual(3, users.Count);

            var thirdUser = users.Find(item => item.UserName == "thirdUser");
            Assert.IsNotNull(thirdUser);
        }

        [TestMethod]
        public void CanSendDeleteToServer()
        {
            var host = new WebServiceHost(typeof(RestSyncService), new Uri("http://localhost:7356"));
            if (host.State != CommunicationState.Opened)
                host.Open();

            SyncStatistics stats = null;
            try
            {
                var uri = new Uri("http://localhost:7356/GetChanges");

                File.Delete("GetChanges.sdf");

                var syncProvider = new RestSyncProvider { ApplyChangesWebMethod = "ApplyChanges", GetChangesWebMethod = "GetChanges", BaseURI = new Uri("http://localhost:7356/") };

                var clientProvider = new SqlCeClientSyncProvider("Data Source = GetChanges.sdf", true);

                DataStoreSchemaBuilder.AddCustomStrategies += (object sender, DataStoreShemaEventArgs args) =>
                {
                    var sqlProvider =
                        (IDataStoreSchemaProvider)sender;

                    sqlProvider.Strategies.Add(
                        new SyncServiceStrategy { SyncBaseType = typeof(SyncTable) });
                };

                ProviderFactory.DefaultProvider = new SqlClientProvider
                {
                    ConnectionString =
                        "Data Source=.;Initial Catalog=RestSyncServiceFixture;Integrated Security=True;",
                    MasterDataBaseConnectionString =
                        "Data Source=.;Initial Catalog=master;Integrated Security=True;"
                };
                DataStoreSchemaBuilder.CreateDataStore(new List<Type> { typeof(User), typeof(User_Tombstone) },
                                                       true);

                var firstUser =(User) new User
                {
                    UserName = "firstUser",
                    Password = "password",
                    FirstName = "first",
                    LastName = "User",
                    CreationDate = DateTime.Now,
                    ModifiedDate = DateTime.Now
                }.Save();

                new User
                {
                    UserName = "secondUser",
                    Password = "password",
                    FirstName = "second",
                    LastName = "User",
                    CreationDate = DateTime.Now,
                    ModifiedDate = DateTime.Now
                }.Save();

                var agent = new mfLYSyncAgent(syncProvider, clientProvider);
                agent.TombstoneBaseType = typeof(Tombstone);

                //add schema builders
                agent.AddSchema(EntityMap.SchemaBuilders, SyncDirection.Bidirectional,
                                TableCreationOption.UploadExistingOrCreateNewTable, "Group1");

                stats = agent.Synchronize();

                var provider = new SqlCeProvider { ConnectionString = "Data Source = GetChanges.sdf" };
                firstUser.Delete();
                firstUser.Save(provider);

                stats = agent.Synchronize();

            }
            finally
            {
                host.Close();
            }

            var users = (List<User>)DataPersistence.Select<User>();

            Assert.AreEqual(2, users.Count);
        }


        [TestMethod]
        public void CanSendDeleteFromServerToClient()
        {
            var host = new WebServiceHost(typeof(RestSyncService), new Uri("http://localhost:7356"));
            if (host.State != CommunicationState.Opened)
                host.Open();

            SyncStatistics stats = null;
            try
            {
                var uri = new Uri("http://localhost:7356/GetChanges");

                File.Delete("GetChanges.sdf");

                var syncProvider = new RestSyncProvider { ApplyChangesWebMethod = "ApplyChanges", GetChangesWebMethod = "GetChanges", BaseURI = new Uri("http://localhost:7356/") };

                var clientProvider = new SqlCeClientSyncProvider("Data Source = GetChanges.sdf", true);

                DataStoreSchemaBuilder.AddCustomStrategies += (object sender, DataStoreShemaEventArgs args) =>
                {
                    var sqlProvider =
                        (IDataStoreSchemaProvider)sender;

                    sqlProvider.Strategies.Add(
                        new SyncServiceStrategy { SyncBaseType = typeof(SyncTable) });
                };

                ProviderFactory.DefaultProvider = new SqlClientProvider
                {
                    ConnectionString =
                        "Data Source=.;Initial Catalog=RestSyncServiceFixture;Integrated Security=True;",
                    MasterDataBaseConnectionString =
                        "Data Source=.;Initial Catalog=master;Integrated Security=True;"
                };
                DataStoreSchemaBuilder.CreateDataStore(new List<Type> { typeof(User), typeof(User_Tombstone) },
                                                       true);

                var firstUser = (User)new User
                {
                    UserName = "firstUser",
                    Password = "password",
                    FirstName = "first",
                    LastName = "User",
                    CreationDate = DateTime.Now,
                    ModifiedDate = DateTime.Now
                }.Save();

                new User
                {
                    UserName = "secondUser",
                    Password = "password",
                    FirstName = "second",
                    LastName = "User",
                    CreationDate = DateTime.Now,
                    ModifiedDate = DateTime.Now
                }.Save();

                var agent = new mfLYSyncAgent(syncProvider, clientProvider);
                agent.TombstoneBaseType = typeof(Tombstone);

                //add schema builders
                agent.AddSchema(EntityMap.SchemaBuilders, SyncDirection.Bidirectional,
                                TableCreationOption.UploadExistingOrCreateNewTable, "Group1");

                stats = agent.Synchronize();

                firstUser.Delete();
                firstUser.Save();

                stats = agent.Synchronize();

            }
            finally
            {
                host.Close();
            }

            var provider = new SqlCeProvider { ConnectionString = "Data Source = GetChanges.sdf" };

            var users = (List<User>)DataPersistence.Select<User>(provider, null);
            Assert.AreEqual(2, users.Count);
        }

        [TestMethod]
        public void CanSendSyncRequestGetChanges()
        {
            var host = new WebServiceHost(typeof (MockRestService), new Uri("http://localhost:7356"));
            if (host.State != CommunicationState.Opened)
                host.Open();

            var uri = new Uri("http://localhost:7356/GetChanges");

            File.Delete("GetChanges.sdf");

            var syncProvider = new RestSyncProvider
                                   {GetChangesWebMethod = "GetChanges", BaseURI = new Uri("http://localhost:7356/")};

            var clientProvider = new SqlCeClientSyncProvider("Data Source = GetChanges.sdf", true);


            DataStoreSchemaBuilder.AddCustomStrategies += (object sender, DataStoreShemaEventArgs args) =>
                                                              {
                                                                  var sqlProvider = (IDataStoreSchemaProvider) sender;

                                                                  sqlProvider.Strategies.Add(new SyncServiceStrategy
                                                                                                 {
                                                                                                     SyncBaseType =
                                                                                                         typeof (
                                                                                                         SyncTable)
                                                                                                 });
                                                              };

            ProviderFactory.DefaultProvider = new SqlClientProvider
                                                  {
                                                      ConnectionString =
                                                          "Data Source=.;Initial Catalog=RestSyncServiceFixture;Integrated Security=True;",
                                                      MasterDataBaseConnectionString =
                                                          "Data Source=.;Initial Catalog=master;Integrated Security=True;"
                                                  };
            DataStoreSchemaBuilder.CreateDataStore(new List<Type> {typeof (User), typeof (User_Tombstone)},
                                                   true);

            for (int i = 0; i < 10; i++)
            {
                var user = new User
                               {
                                   UserName = "testUser" + i,
                                   Password = "password",
                                   FirstName = "Test",
                                   LastName = "User" + i,
                                   CreationDate = DateTime.Now,
                                   ModifiedDate = DateTime.Now
                               };
                user.Save();
            }

            var agent = new mfLYSyncAgent(syncProvider, clientProvider);
            agent.TombstoneBaseType = typeof (Tombstone);

            //add schema builders
            agent.AddSchema(EntityMap.SchemaBuilders, SyncDirection.Bidirectional,
                            TableCreationOption.UploadExistingOrCreateNewTable, "Group1");

            SyncStatistics stats = agent.Synchronize();

            host.Close();

            Assert.IsNotNull(MockRestService.Request);
            Assert.IsNotNull(MockRestService.Request.GroupMetadata);
            Assert.IsNotNull(MockRestService.Request.Session);
        }

        [TestMethod]
        public void CreateSimpleSyncSchema()
        {
            File.Delete("SimpleSyncSchema.Sql");

            ProviderFactory.DefaultProvider = new SqlClientProvider();

            using (var writer = new FileStream("SimpleSyncSchema.Sql", FileMode.CreateNew))
            {
                var syncStrategy = new SyncServiceStrategy {SyncBaseType = typeof (SyncTable)};

                using (StreamWriter stream = DataStoreSchemaBuilder.GenerateDDLCreateScripts(writer, true,
                                                                                             new List<Type>
                                                                                                 {
                                                                                                     typeof (User),
                                                                                                     typeof (User_Tombstone
                                                                                                         )
                                                                                                 },
                                                                                             syncStrategy))
                {
                    stream.Flush();
                }
            }
        }

        #endregion

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext { get; set; }
    }

    public class MockRestService : IRestSyncService
    {
        #region Properties

        public static mfLYSyncRequest Request { get; set; }

        #endregion

        #region IRestSyncService Members

        public mfLYSyncResponse GetChanges(mfLYSyncRequest syncRequest)
        {
            Request = syncRequest;
            return null;
        }

        #endregion

        #region IRestSyncService Members


        public mfLYSyncResponse ApplyChanges(mfLYSyncRequest syncRequest)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}